#include <iostream>
#include <list>
//#include <stringstream>
#include "GridData/GridSingleton.hpp"
#include <time.h>

#define MAX_LEN 1024

static const std::string clouds("clouds");
static const std::string customer("customer");
static const std::string mark("mark");
static const std::string mstreet("mstreet");

static const std::string ret_error("ERROR");
static const std::string ret_right("RIGHT");
static const std::string ret_store("STORE");

template<class Ty>
void ShowedReduce(std::list<Ty> data_list, int zoom)
{
	typename Ty::BaseData;

	std::list<Ty>::iterator it = data_list.begin(); 
	for (; it != data_list.end();)
	{
		if (it->getMinShowedZoom() <= zoom)
		{
			it++;
		}
		else
		{
			data_list.erase(it++);
		}
	}
}

template<class Ty, class Data_Map>
void TimeinReduce(std::list<Ty> data_list, int ava_time,Data_Map& d_map)
{
	std::list<Ty>::iterator it = data_list.begin();
	for (;it != data_list.end();)
	{
		time_t time_;
		time(&time_);
		if ((time_ - it->getTimein()) > ava_time)
		{
			d_map.remove(it->getKey());
			data_list.erase(it++);
		}
		else
		{
			it++;
		}
	}
}


void do_clouds_set(std::istringstream& strcommand, std::ostringstream& strreslut)
{
	double lng;
	double lat;
	double height;
	int speed;
	int direction;
	int color;
	std::string jid;
	int min_showed_zoom = -1;

	strcommand >> lng >> lat >> height >> speed >> direction >> color >> jid >> min_showed_zoom;

	if (min_showed_zoom == -1)
	{
		strreslut << ret_error;
	}
	else
	{
		CloudPointData data(lng, lat, height, speed, direction, color, jid);
		data.setMinShowedZoom(min_showed_zoom);
		GridSingleton::getInstance()->getCloudsMap().insert(jid, data);
		strreslut << ret_store;
	}
}

void do_customer_set(std::istringstream& strcommand, std::ostringstream& strreslut)
{
	double lng;
	double lat;
	std::string jid;
	int pictype;
	time_t pictime;
	std::string picname;
	int min_showed_zoom = -1;

	strcommand >> lng >> lat >> jid  >> pictype >> pictime >> picname >> min_showed_zoom;


	if (min_showed_zoom == -1)
	{
		strreslut << ret_error;
	}
	else
	{
		CustomerPicData data(lng, lat, jid, picname, pictime, pictype);
		data.setMinShowedZoom(min_showed_zoom);
		GridSingleton::getInstance()->getCustomerMap().insert(picname, data);
		strreslut << ret_store;
	}
}
void do_mark_set(std::istringstream& strcommand, std::ostringstream& strreslut)
{
	double lng;
	double lat;
	int mark_type;
	int direction;
	std::string memo;
	int mark_id;
	int min_showed_zoom = -1;

	strcommand >> lng >> lat  >> mark_type >> direction >> memo >> mark_id >> min_showed_zoom;

	if (min_showed_zoom == -1)
	{
		strreslut << ret_error;
	}
	else
	{
		MarkPicData data(lng, lat, mark_id, mark_type, direction, memo);
		data.setMinShowedZoom(min_showed_zoom);
		GridSingleton::getInstance()->getMarkMap().insert(mark_id, data);
		strreslut << ret_store;
	}

}
void do_mstreet_set(std::istringstream& strcommand, std::ostringstream& strreslut)
{
	double lng;
	double lat;
	double height;
	int speed;
	int direction;
	std::string city;
	time_t time;
	int pic_id;
	std::string jid;
	int min_showed_zoom = -1;

	strcommand >> lng >> lat >> height >> speed >> direction >> city >> time >> pic_id >> jid >> min_showed_zoom;

	if (min_showed_zoom == -1)
	{
		strreslut << ret_error;
	}
	else
	{	
		MStreetData mstreet_data(lng, lat, height, speed, direction, city, pic_id, time, jid);
		mstreet_data.setMinShowedZoom(min_showed_zoom);
		GridSingleton::getInstance()->getMStreetMap().insert(pic_id, mstreet_data);

		strreslut << ret_store;
	}


}

void do_clouds_get(std::istringstream& strcommand, std::ostringstream& strreslut)
{
	int x;
	int y;
	int zoom;

	strcommand >> zoom >> x >> y ;

	if (zoom > maxzoom || zoom < minzoom)
	{
		strreslut << ret_error;
		return ;
	}

	GRIDAlgorithm::GRID grid(zoom, x, y, 0);
	std::list<CloudPointData> data_list;
	GridSingleton::getInstance()->getCloudsMap().getDataByGrid(grid, data_list);

	TimeinReduce(data_list, 15, GridSingleton::getInstance()->getCloudsMap());
	ShowedReduce(data_list, zoom);

	strreslut << ret_right << std::endl;

	strreslut << data_list.size() << "\n";

	{
		std::list<CloudPointData>::iterator it = data_list.begin();
		for (;it != data_list.end(); it++)
		{
			static char buffer[MAX_LEN];

			sprintf_s(buffer, "%.6lf %.6lf %.6lf %d %d %d %s\n",
				it->getLng(), it->getLat(), it->getHeight(),
				it->getSpeed(), it->getDirection(), it->getColor(),
				it->getJID().c_str());
			
			strreslut << buffer;
		}
	}
}

void do_customer_get(std::istringstream& strcommand, std::ostringstream& strresult)
{
	int x;
	int y;
	int zoom;
	int pictime;
	int pictype;

	strcommand >> zoom >> x >> y >> pictime >> pictype;

	if (zoom > maxzoom || zoom < minzoom)
	{
		strresult << ret_error;
		return;
	}

	GRIDAlgorithm::GRID grid(zoom, x, y, 0);
	std::list<CustomerPicData> data_list;
	GridSingleton::getInstance()->getCustomerMap().getDataByGrid(grid, data_list);

	TimeinReduce(data_list, 7 * 24 * 60 * 60, GridSingleton::getInstance()->getCustomerMap());
	ShowedReduce(data_list, zoom);

	strresult << ret_right << std::endl;

	strresult << data_list.size() << "\n";

	{
		std::list<CustomerPicData>::iterator it = data_list.begin();
		for (;it != data_list.end(); it++)
		{
			static char buffer[MAX_LEN];

			sprintf_s(buffer, "%.6lf %.6lf %s %d %I64d %s\n",
				it->getLng(),
				it->getLat(),
				it->getJid().c_str(),
				it->getPicType(),
				it->getTime(),
				it->getPicname().c_str());

			strresult << buffer;
		}
	}
}
void do_mark_get(std::istringstream& strcommand, std::ostringstream& strresult)
{
	int x;
	int y;
	int zoom;

	strcommand >> zoom >> x >> y;
	if (zoom > maxzoom || zoom < minzoom)
	{
		strresult << ret_error;
		return ;
	}
	GRIDAlgorithm::GRID grid(zoom, x, y, 0);

	std::list<MarkPicData> data_list;
	GridSingleton::getInstance()->getMarkMap().getDataByGrid(grid, data_list);

	ShowedReduce(data_list, zoom);

	strresult << ret_right << std::endl;

	strresult << data_list.size() << "\n";

	{
		std::list<MarkPicData>::iterator it = data_list.begin();
		for (;it != data_list.end(); it++)
		{
			static char buffer[MAX_LEN];

			sprintf_s(buffer, "%.6lf %.6lf %d %d %d %s\n",
				it->getLng(),
				it->getLat(),
				it->getMarkID(),
				it->getMarkType(),
				it->getDirection(),
				it->getMemo().c_str());

			strresult << buffer;
		}
	}
}
void do_mstreet_get(std::istringstream& strcommand, std::ostringstream& strresult)
{
	int x;
	int y;
	int zoom;

	strcommand >> zoom >> x >> y;
	if (zoom > maxzoom || zoom < minzoom)
	{
		strresult << ret_error;
		return ;
	}
	GRIDAlgorithm::GRID grid(zoom, x, y, 0);

	std::list<MStreetData> data_list;
	GridSingleton::getInstance()->getMStreetMap().getDataByGrid(grid, data_list);

	TimeinReduce(data_list, 7 * 24 * 60 * 60, GridSingleton::getInstance()->getMStreetMap());
	ShowedReduce(data_list, zoom);

	strresult << ret_right << std::endl;

	strresult << data_list.size() << "\n";

	{
		std::list<MStreetData>::iterator it = data_list.begin();
		for (;it != data_list.end(); it++)
		{
			static char buffer[MAX_LEN];

			sprintf_s(buffer, "%.6lf %.6lf %.6lf %d %d %s %d %I64d %s\n",
				it->getLng(),
				it->getLat(),
				it->getHeight(),
				it->getSpeed(),
				it->getDirection(),
				it->getCity().c_str(),
				it->getPicID(),
				it->getTime(),
				it->getJid().c_str());

			strresult << buffer;
		}
	}
}

void do_mark_remove(std::istringstream& strcommand, std::ostringstream& strresult)
{
	int mark_id;
	strcommand >> mark_id;
	
	GridSingleton::getInstance()->getMarkMap().remove(mark_id);

	strresult << ret_right;

}

void do_set(std::istringstream& strcommand, std::ostringstream& strresult)
{
	std::string db_name;
	strcommand >> db_name;

	if (db_name == "clouds")
	{
	 	do_clouds_set(strcommand, strresult);
	}
	else if (db_name == "customer")
	{
		do_customer_set(strcommand, strresult);
	}
	else if (db_name == "mark")
	{
		do_mark_set(strcommand, strresult);
	}
	else if (db_name == "mstreet")
	{
		do_mstreet_set(strcommand, strresult);
	}
	else
	{
		strresult << ret_error;
	}
}

void do_clear(std::istringstream& strcommand, std::ostringstream& strresult)
{
	std::string db_name;
	strcommand >> db_name;

	if (db_name == clouds)
	{
		GridSingleton::getInstance()->getCloudsMap().clear();
	}
	else if (db_name == customer)
	{
		GridSingleton::getInstance()->getCustomerMap().clear();
	}
	else if (db_name == mark)
	{
		GridSingleton::getInstance()->getMarkMap().clear();
	}
	else if (db_name == mstreet)
	{
		GridSingleton::getInstance()->getMStreetMap().clear();
	}
	else
	{
		strresult << ret_error;
		return;
	}
	strresult << ret_right;
}

void do_get(std::istringstream& strcommand, std::ostringstream& strresult)
{
	std::string db_name;
	strcommand >> db_name;

	if (db_name == clouds)
	{
		do_clouds_get(strcommand, strresult);
	}
	else if (db_name == customer)
	{
		do_customer_get(strcommand, strresult);
	}
	else if (db_name == mark)
	{
		do_mark_get(strcommand, strresult);
	}
	else if (db_name == mstreet)
	{
		do_mstreet_get(strcommand, strresult);
	}
	else
	{
		strresult << ret_error;
	}
}
void do_remove(std::istringstream& strcommand, std::ostringstream& strresult)
{
	std::string db_name;
	strcommand >> db_name;

	if (db_name == mark)
	{
		do_mark_remove(strcommand, strresult);
	}
	else
	{
		strresult << ret_error;
	}
}


void do_query(std::string& input_, std::string& output_)
{
	std::istringstream strcommand;
	strcommand.str(input_);
	std::ostringstream strreslut;
	strreslut.str(output_);

	std::string command;

	strcommand >> command;

	if (command == "set")
	{
		do_set(strcommand, strreslut);
	}
	else if (command == "get")
	{
		do_get(strcommand, strreslut);
	}
	else if (command == "clear")
	{
		do_clear(strcommand, strreslut);
	}
	else if (command == "remove")
	{
		do_remove(strcommand, strreslut);
	}
	else
	{
		strreslut << ret_error;
	}
	output_ = strreslut.str();
	
}

